package cc.chenwenxi.zb.rest.security;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringJoiner;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;

import cc.chenwenxi.zb.rest.domain.config.ZbApiConfig;
import cn.hutool.core.convert.Convert;
import kits.my.EncryDigestUtil;
import kits.my.MapSort;
import lombok.Data;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

@Data
public class AuthenticationInterceptor implements Interceptor {

	private final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36";

	private ZbApiConfig config;

	public AuthenticationInterceptor(ZbApiConfig config) {
		super();
		this.config = config;
	}

	@Override
	public Response intercept(Chain chain) throws IOException {
		Request original = chain.request();
		Request.Builder newRequestBuilder = original.newBuilder();
		newRequestBuilder.addHeader("User-Agent", USER_AGENT);
		if (original.method().equalsIgnoreCase("POST")) {
			newRequestBuilder.addHeader("Content-Type", "application/json");
		} else if (original.method().equalsIgnoreCase("GET")) {
			newRequestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded");
		}

		StringBuilder sb = new StringBuilder(1024);
        sb.append(original.url().scheme()).append("://")
                .append(original.url().host())
                .append(original.url().uri().getPath());
        this.buildForGet(original, sb);

		Request newRequest = newRequestBuilder.url(sb.toString()).build();
		chain.withConnectTimeout(5, TimeUnit.SECONDS);
		chain.withReadTimeout(5, TimeUnit.SECONDS);
		chain.withWriteTimeout(5, TimeUnit.SECONDS);
		return chain.proceed(newRequest);
	}

	private void buildForGet(Request request, StringBuilder sb) {

		StringJoiner joiner = new StringJoiner("&");
		// 参数排序
		TreeSet<String> names = new TreeSet(request.url().queryParameterNames());
		Map<String, Object> params = new LinkedHashMap<>();
		params.put("accesskey", config.getApiKey());// 需要加入签名,放前面
		// 拼接
		for (String key : names) {
			String value = request.url().queryParameter(key);
			joiner.add(key + '=' + urlEncode(value));
			params.put(key, value);
		}

		String digest = EncryDigestUtil.digest(config.getSecretKey());
		String sign = EncryDigestUtil.hmacSign(toStringMap(params), digest);

		joiner.add("accesskey=" + config.getApiKey());
		joiner.add("sign=" + sign);
		joiner.add("reqTime=" + Convert.toStr(System.currentTimeMillis()));
		// 追加问号
		if (!StringUtils.isEmpty(joiner.toString())) {
			sb.append("?");
		}
		sb.append(joiner.toString());
	}

	/**
	 * 使用标准URL Encode编码。注意和JDK默认的不同，空格被编码为%20而不是+。
	 *
	 * @param s String字符串
	 * @return URL编码后的字符串
	 */
	private String urlEncode(String s) {
		try {
			return URLEncoder.encode(s, "UTF-8").replaceAll("\\+", "%20");
		} catch (UnsupportedEncodingException e) {
			throw new IllegalArgumentException("UTF-8 encoding not supported!");
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private String toStringMap(Map m) {
		// 按map键首字母顺序进行排序
		m = MapSort.sortMapByKey(m);
		StringBuilder sbl = new StringBuilder();
		for (Iterator<Entry> i = m.entrySet().iterator(); i.hasNext();) {
			Entry e = i.next();
			Object o = e.getValue();
			String v = "";
			if (o == null) {
				v = "";
			} else if (o instanceof String[]) {
				String[] s = (String[]) o;
				if (s.length > 0) {
					v = s[0];
				}
			} else {
				v = o.toString();
			}
			if (!e.getKey().equals("sign") && !e.getKey().equals("reqTime") && !e.getKey().equals("tx")) {
				sbl.append("&").append(e.getKey()).append("=").append(v);
			}
		}
		String s = sbl.toString();
		if (s.length() > 0) {
			return s.substring(1);
		}
		return "";
	}

}
